package ln.gather.jsr.jndi.file;

import ln.gather.jsr.jndi.function.ThrowableAction;
import ln.gather.jsr.jndi.function.ThrowableSupplier;
import ln.gather.jsr.common.util.Deserializer;
import ln.gather.jsr.common.util.Deserializers;
import ln.gather.jsr.common.util.Serializer;
import ln.gather.jsr.common.util.Serializers;
import org.apache.commons.io.FileUtils;

import javax.naming.*;
import java.io.File;
import java.util.Hashtable;

public class FileSystemContext implements Context {

    private final static String CONTEXT_NAME_PREFIX = "java:";

    private final static String ENV_CONTEXT_NAME = "java:comp/env";

    /**
     * 序列化
     */
    private final static Serializers serializers = new Serializers();

    /**
     * 反序列化
    */
    private final static Deserializers deserializers = new Deserializers();

    static {
        serializers.loadSPI();
        deserializers.loadSPI();
    }

    private final File rootDirectory;

    private final Hashtable<Object, Object> environment;

    public FileSystemContext(File rootDirectory, Hashtable<?, ?> environment) {
        this.rootDirectory = rootDirectory;
        if(!rootDirectory.exists()) {
            rootDirectory.mkdirs();
        }
        this.environment = (Hashtable<Object, Object>) environment.clone();
    }

    public FileSystemContext(Hashtable<?, ?> environment) {
        this(getRootDirectory(environment), environment);
    }

    private static File getRootDirectory(Hashtable<?, ?> environment) {
        final File rootDirectory;
        String dirPath = (String) environment.get("jndi.file.root.dir.path");
        if(dirPath != null) {
            rootDirectory = new File(dirPath);
        } else {
            rootDirectory = new File(System.getProperty("java.io.tmpdir"), ".jndi");
        }
        return rootDirectory;
    }

    @Override
    public Object lookup(Name name) throws NamingException {
        return lookup(name.toString());
    }

    @Override
    public Object lookup(String name) throws NamingException {
        int index = name.indexOf(CONTEXT_NAME_PREFIX);
        String targetName = name;
        if(index > -1) {
            targetName = name.substring(CONTEXT_NAME_PREFIX.length());
        }
        File targetFile = targetFile(targetName);

        if(ENV_CONTEXT_NAME.equals(name) || targetFile.isDirectory()) {
            return createSubcontext(targetFile);
        } else if(targetFile.exists()) {
            return lookup(targetFile);
        }
        return null;
    }

    private Object lookup(File targetFile) throws NamingException {
        return ThrowableSupplier.execute(() -> {
            byte[] bytes = FileUtils.readFileToByteArray(targetFile);
            Deserializer deserializer = deserializers.getMostCompatible(String.class);
            return deserializer.deserialize(bytes);
        }, NamingException.class);
    }

    private Object createSubcontext(File targetFile) {
        return new FileSystemContext(targetFile, this.environment);
    }

    private File targetFile(String targetName) {
        return new File(rootDirectory, targetName);
    }

    @Override
    public void bind(Name name, Object obj) throws NamingException {

    }

    @Override
    public void bind(String name, Object obj) throws NamingException {
        bind(name, obj, false);
    }

    private void bind(String name, Object obj, boolean override) throws NamingException {
        File targetFile = targetFile(name);
        if(override && !targetFile.exists()) {
            return;
        }
        ThrowableAction.execute(() -> {
            Class<?> objClass = obj.getClass();
            Serializer serializer = serializers.getMostCompatible(objClass);
            byte[] bytes = serializer.serialize(obj);
            FileUtils.writeByteArrayToFile(targetFile, bytes);
        }, NamingException.class);
    }

    @Override
    public void rebind(Name name, Object obj) throws NamingException {

    }

    @Override
    public void rebind(String name, Object obj) throws NamingException {

    }

    @Override
    public void unbind(Name name) throws NamingException {

    }

    @Override
    public void unbind(String name) throws NamingException {

    }

    @Override
    public void rename(Name oldName, Name newName) throws NamingException {

    }

    @Override
    public void rename(String oldName, String newName) throws NamingException {

    }

    @Override
    public NamingEnumeration<NameClassPair> list(Name name) throws NamingException {
        return null;
    }

    @Override
    public NamingEnumeration<NameClassPair> list(String name) throws NamingException {
        File targetFile = targetFile(name);
        if(targetFile.exists() && targetFile.isDirectory()) {

        }
         return null;
    }

    @Override
    public NamingEnumeration<Binding> listBindings(Name name) throws NamingException {
        return null;
    }

    @Override
    public NamingEnumeration<Binding> listBindings(String name) throws NamingException {
        return null;
    }

    @Override
    public void destroySubcontext(Name name) throws NamingException {

    }

    @Override
    public void destroySubcontext(String name) throws NamingException {

    }

    @Override
    public Context createSubcontext(Name name) throws NamingException {
        return null;
    }

    @Override
    public Context createSubcontext(String name) throws NamingException {
        return null;
    }

    @Override
    public Object lookupLink(Name name) throws NamingException {
        return null;
    }

    @Override
    public Object lookupLink(String name) throws NamingException {
        return null;
    }

    @Override
    public NameParser getNameParser(Name name) throws NamingException {
        return null;
    }

    @Override
    public NameParser getNameParser(String name) throws NamingException {
        return null;
    }

    @Override
    public Name composeName(Name name, Name prefix) throws NamingException {
        return null;
    }

    @Override
    public String composeName(String name, String prefix) throws NamingException {
        return null;
    }

    @Override
    public Object addToEnvironment(String propName, Object propVal) throws NamingException {
        return null;
    }

    @Override
    public Object removeFromEnvironment(String propName) throws NamingException {
        return null;
    }

    @Override
    public Hashtable<?, ?> getEnvironment() throws NamingException {
        return null;
    }

    @Override
    public void close() throws NamingException {

    }

    @Override
    public String getNameInNamespace() throws NamingException {
        return null;
    }
}
